using System;
using System.Runtime.CompilerServices;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize.Formatters
{
    // for unamanged types( https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types )
    // * sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool
    // * Any enum type
    // * Any pointer type
    // * Any user-defined struct type that contains fields of unmanaged types only
    [Preserve]
    public sealed unsafe class UnmanagedFormatter64<T> : LFormatter<T>
        where T : unmanaged
    {
        [Preserve]
        public override void Serialize(LSerializeContext* context, LStreamWriter* writer, ref T value)
        {
            Profiler.Start("UnmanagedFormatter64.Serialize");
            // Assert.Check(sizeof(T) == 8, "UnmanagedFormatter64<T> only support 64 bit type.");
            var value64 = Unsafe.As<T, ulong>(ref value);
            writer->WriteUInt64(value64);
            // fixed (void* ptr = &value)
            // {
            //     writer->WriteUInt64(*(ulong*) ptr);
            // }
            Profiler.End();
        }
    
        [Preserve]
        public override void Deserialize(LSerializeContext* context, LStreamReader* reader, ref T value)
        {
            Profiler.Start("UnmanagedFormatter64.Deserialize");
            // Assert.Check(sizeof(T) == 8, "UnmanagedFormatter64<T> only support 64 bit type.");
            var value64 = reader->ReadUInt64();
            value = Unsafe.As<ulong, T>(ref value64);
            // fixed (void* ptr = &value)
            // {
            //     *(ulong*) ptr = reader->ReadUInt64();
            //     value = *(T*) ptr;
            // }
            Profiler.End();
        }
    }
    
    [Preserve]
    public sealed unsafe class UnmanagedFormatter128<T> : LFormatter<T>
        where T : unmanaged
    {
        [Preserve]
        public override void Serialize(LSerializeContext* context, LStreamWriter* writer, ref T value)
        {
            Profiler.Start("UnmanagedFormatter128.Serialize");
            Assert.Check(sizeof(T) == 16, "UnmanagedFormatter128<T> only support 128 bit type.");
            // var size = sizeof(T);
            // if (size != 16) return;
            // fixed (void* ptr = &value)
            // {
            //     writer->WriteUInt64(*((ulong*) ptr + 0));
            //     writer->WriteUInt64(*((ulong*) ptr + 1));
            // }
            writer->WriteUnmanagedValue<T>(ref value);
            Profiler.End();
        }
    
        [Preserve]
        public override void Deserialize(LSerializeContext* context, LStreamReader* reader, ref T value)
        {
            Profiler.Start("UnmanagedFormatter128.Deserialize");
            Assert.Check(sizeof(T) == 16, "UnmanagedFormatter128<T> only support 128 bit type.");
            // if (sizeof(T) != 16) return;
            // fixed (void* ptr = &value)
            // {
            //     *((ulong*) ptr + 0) = reader->ReadUInt64();
            //     *((ulong*) ptr + 1) = reader->ReadUInt64();
            //     value = *(T*) ptr;
            // }
            reader->ReadUnmanagedValue<T>(ref value);
            Profiler.End();
        }
    }
    
    [Preserve]
    public sealed unsafe class UnmanagedFormatter<T> : LFormatter<T>
        where T : unmanaged
    {
        [Preserve]
        public override void Serialize(LSerializeContext* context, LStreamWriter* writer, ref T value)
        {
            Profiler.Start("UnmanagedFormatter.Serialize");
            var size = sizeof(T);
            fixed (void* ptr = &value)
            {
                // writer->WriteBytes((byte *)ptr, size);
                writer->WriteUnmanagedValue((byte*)ptr, size);
            }
            Profiler.End();
        }
    
        [Preserve]
        public override void Deserialize(LSerializeContext* context, LStreamReader* reader, ref T value)
        {
            // var bytes = reader->ReadBytes();
            // fixed(byte* ptr = &bytes[0])
            // {
            //     value = *(T*) ptr;
            // }
            Profiler.Start("UnmanagedFormatter.Deserialize");
            reader->ReadUnmanagedValue<T>(ref value);
            Profiler.End();
        }
    }
    
    [Preserve]
    public sealed unsafe class UnsafeUnmanagedFormatter<T> : LFormatter<T>
        where T : unmanaged
    {
        [Preserve]
        public override void Serialize(LSerializeContext* context, LStreamWriter* writer, ref T value)
        {
            writer->UnsafeWriteUnmanagedValue<T>(ref value);
        }
    
        [Preserve]
        public override void Deserialize(LSerializeContext* context, LStreamReader* reader, ref T value)
        {
            reader->UnsafeReadUnmanagedValue<T>(ref value);
        }
    }
}